热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

前文|功能型_品读鸿蒙HDF架构

篇首语:本文由编程笔记#小编为大家整理,主要介绍了品读鸿蒙HDF架构相关的知识,希望对你有一定的参考价值。 品读鸿蒙HDF架构(三)            侯 亮 现在我们

篇首语:本文由编程笔记#小编为大家整理,主要介绍了品读鸿蒙HDF架构相关的知识,希望对你有一定的参考价值。



品读鸿蒙HDF架构(三)

           侯 亮

现在我们继续研究鸿蒙HDF架构,上回书说到经由HdfDeviceAttach(),HdfDevice节点不但添加进了DevHostService的devices列表,而且还和一个DeviceNodeExt联系起来了,呈现的示意图大致如下:

接着,HdfDeviceAttach()最后会调用nodeIf->LaunchNode(),这一步实际上调用的是HdfDeviceLaunchNode(),代码截选如下:
【drivers/hdf/frameworks/core/host/src/Hdf_device_node.c】

int HdfDeviceLaunchNode(struct HdfDeviceNode *devNode, struct IHdfDevice *devInst)
    struct HdfDevice *device = (struct HdfDevice *)devInst;
    . . . . . .
    struct HdfDriverEntry *driverEntry = devNode->driverEntry;
    const struct HdfDeviceInfo *deviceInfo = devNode->deviceInfo;
    struct IHdfDeviceToken *deviceToken = NULL;
    . . . . . .
    int ret = driverEntry->Init(&devNode->deviceObject);
    . . . . . .
    ret = HdfDeviceNodePublishService(devNode, deviceInfo, devInst);
    . . . . . .
    deviceToken = devNode->token;
    ret = DevmgrServiceClntAttachDevice(deviceInfo, deviceToken);
    . . . . . .
    return ret;

“Launch”本就是启动之意,在这里就是指启动与HdfDevice对应的驱动服务。所以此处会先调用一下driverEntry->Init(),并传入参数&devNode->deviceObject。这意味着要求驱动程序在初始化时,回填一下deviceObject里的service域。

接着,主要执行了两个动作:
1)发布驱动服务
2)挂接设备
我们会分两小节来阐述。


1 发布驱动服务

初始化完成后,就可以“发布”这个驱动服务了,HdfDeviceNodePublishService()的代码截选如下:
【drivers/hdf/frameworks/core/host/src/Hdf_device_node.c】

static int HdfDeviceNodePublishService(struct HdfDeviceNode *devNode, 
                                       const struct HdfDeviceInfo *deviceInfo, 
                                       struct IHdfDevice *device)
    int status = HDF_SUCCESS;
    . . . . . .
    struct IDeviceNode *nodeIf = &devNode->super;
    if ((deviceInfo->policy == SERVICE_POLICY_PUBLIC) ||
        (deviceInfo->policy == SERVICE_POLICY_CAPACITY))
        if (nodeIf->PublishService != NULL)
// 其实调用的是 DeviceNodeExtPublishService()
            status = nodeIf->PublishService(devNode, deviceInfo->svcName);
       
   
    if (status == HDF_SUCCESS)
        status = HdfDeviceNodePublishLocalService(devNode, deviceInfo);
   
    return status;

从字面上理解,如果一个设备是“公共型”或“功能型”的,则会调用nodeIf->PublishService()发布对应的驱动服务。而我们以前研究过,在HdfDeviceNode构造之时,我们可以看到为PublishService域设定了DeviceNodeExtPublishService()函数指针,因此上面代码中调用PublishService的地方,其实就是在调用这个函数。

【drivers/hdf/lite/manager/src/Hdf_device_node_ext.c】

static int DeviceNodeExtPublishService(struct HdfDeviceNode *inst, const char *serviceName)
    const struct HdfDeviceInfo *deviceInfo = NULL;
    struct HdfDeviceObject *deviceObject = NULL;
    struct DeviceNodeExt *devNodeExt = (struct DeviceNodeExt *)inst;
    . . . . . .
    int ret = HdfDeviceNodePublishPublicService(inst, serviceName);
    . . . . . .
    deviceInfo = inst->deviceInfo;
    deviceObject = &devNodeExt->super.deviceObject;
    . . . . . .
    if (deviceInfo->policy == SERVICE_POLICY_CAPACITY)
        devNodeExt->ioservice = HdfIoServiceBind(serviceName, deviceInfo->permission);
        if (devNodeExt->ioService != NULL)
            devNodeExt->ioService->target = (struct HdfObject*)(&inst->deviceObject);
            static struct HdfIoDispatcher dispatcher =
                .Dispatch = DeviceNodeExtDispatch
            ;
            devNodeExt->ioService->dispatcher = &dispatcher;
        else
            . . . . . .
       
   
    return HDF_SUCCESS;

这样说来,发布服务时其实分了两个部分,一个是发布public部分,一个是发布local部分。分别对应HdfDeviceNodePublishPublicService()和HdfDeviceNodePublishLocalService()。


1.1 发布Public Service部分

【drivers/hdf/frameworks/core/host/src/Hdf_device_node.c】

int HdfDeviceNodePublishPublicService(struct HdfDeviceNode *devNode, const char *svcName)
    if ((devNode == NULL) || (devNode->deviceObject.service == NULL))
        HDF_LOGE("device method is null");
        return HDF_FAILURE;
   
    return DevSvcManagerClntAddService(svcName, &devNode->deviceObject);

【drivers/hdf/frameworks/core/host/src/Devsvc_manager_clnt.c】

int DevSvcManagerClntAddService(const char *svcName, struct HdfDeviceObject *service)
    struct DevSvcManagerClnt *devSvcMgrClnt = DevSvcManagerClntGetInstance();
    . . . . . .
    struct IDevSvcManager *serviceManager = devSvcMgrClnt->devSvcMgrIf;
. . . . . .
// 其实调用的是 DevSvcManagerAddService()
    return serviceManager->AddService(serviceManager, svcName, service);

【drivers/hdf/frameworks/core/host/src/Devsvc_manager_clnt.c】

struct DevSvcManagerClnt *DevSvcManagerClntGetInstance()
    static struct DevSvcManagerClnt *instance = NULL;
    if (instance == NULL)
        static struct DevSvcManagerClnt singletonInstance;
        DevSvcManagerClntConstruct(&singletonInstance);
        instance = &singletonInstance;
   
    return instance;

从代码看,系统中有一个“设备服务管理器”(DevSvcManager),那些功能型设备都会把自己注册进它。这个倒有点儿像android里的SMS(Service Manager Service),所有系统核心服务都会向SMS里注册自己,以便其他应用可以从SMS查询并获取服务代理。实际上,鸿蒙系统在不少方面倒的确和Android有一定类比性,这个以后我们再对比看看,目前先放下不谈。

注册设备服务的那句serviceManager->AddService()实际上调用的是DevSvcManagerAddService(),该函数会尝试向“设备服务管理器”里添加一个管理节点(DevSvcRecord):
【drivers/hdf/frameworks/core/manager/src/Devsvc_manager.c】

int DevSvcManagerAddService(struct IDevSvcManager *inst, const char *svcName, struct HdfDeviceObject *service)
    struct DevSvcManager *devSvcManager = (struct DevSvcManager *)inst;
    . . . . . .
    struct DevSvcRecord *record = DevSvcRecordNewInstance();
    . . . . . .
    record->key = HdfStringMakeHashKey(svcName, 0);
    record->value = service;
    OsalMutexLock(&devSvcManager->mutex);
    HdfSListAdd(&devSvcManager->services, &record->entry);
    OsalMutexUnlock(&devSvcManager->mutex);
return HdfServiceObserverPublishService(&devSvcManager->observer, svcName, 0, SERVICE_POLICY_PUBLIC, (struct HdfObject *)service->service);

【drivers/hdf/frameworks/core/host/src/Hdf_service_observer.c】

int HdfServiceObserverPublishService(struct HdfServiceObserver *observer,
    const char *svcName, uint32_t matchId, uint16_t policy, struct HdfObject *service)
    struct HdfServiceObserverRecord *serviceRecord = NULL;
    uint32_t serviceKey = HdfStringMakeHashKey(svcName, 0);
    . . . . . .
serviceRecord = (struct HdfServiceObserverRecord *)HdfSListSearch(&observer->services, serviceKey, HdfServiceObserverRecordCompare);
    if (serviceRecord == NULL)
        serviceRecord = HdfServiceObserverRecordObtain(serviceKey);
        if (serviceRecord == NULL)
            HDF_LOGE("PublishService failed, serviceRecord is null");
            return HDF_FAILURE;
       
        serviceRecord->publisher = service;
        serviceRecord->matchId = matchId;
        serviceRecord->policy = policy;
        HdfSListAdd(&observer->services, &serviceRecord->entry);
    else
        serviceRecord->publisher = service;
        HdfServiceObserverRecordNotifySubscribers(serviceRecord, matchId, policy);
   
    return HDF_SUCCESS;

对于“设备服务管理器”而言,当它要管理一个设备服务时,主要需要两个Record:
1)DevSvcRecord:每个服务对应一个DevSvcRecord,这个节点会插入DevSvcManager内部的services链表。如果发布服务时,发现对应的DevSvcRecord已经存在了,则会向所有订阅者发出通知。
2)HdfServiceObserverRecord:每个服务对应一个HdfServiceObserverRecord,这个节点会插入DevSvcManager内的observer部分(内部的services链表)里。每个HdfServiceObserverRecord负责维护一个“订阅者”链表,记录所有对该服务感兴趣的订阅者。

DevSvcRecord的value域是个HdfDeviceObject *指针,其实指向的就是和设备对应的DeviceNodeExt节点的deviceObject部分,根据我们以前储备的知识,我们知道这个deviceObject部分的service域指向的就是设备驱动实现的IDeviceIoService接口。

另外,从前面代码的serviceRecord->publisher = service一句,可以看到HdfServiceObserverRecord的publisher域,其实也是指向设备驱动实现的IDeviceIoService接口的。这样我们可以绘制如下的示意图:

当然,一开始HdfServiceObserverRecord里是“订阅者”链表为空啦,不过日后如果有其他服务注册为订阅者了,HdfServiceObserverRecordNotifySubscribers()就可以向它们发送通知了。发通知函数的代码如下:
【drivers/hdf/frameworks/core/host/src/Hdf_observer_record.c】

void HdfServiceObserverRecordNotifySubscribers(struct HdfServiceObserverRecord *record, uint32_t matchId, uint16_t policy)
    struct HdfSListIterator it;
    . . . . . .
    OsalMutexLock(&record->obsRecMutex);
    HdfSListIteratorInit(&it, &record->subscribers);
    while (HdfSListIteratorHasNext(&it))
        struct HdfServiceSubscriber *subscriber = (struct HdfServiceSubscriber *)HdfSListIteratorNext(&it);
        if ((matchId == subscriber->matchId) || (policy != SERVICE_POLICY_PRIVATE))
            subscriber->state = HDF_SUBSCRIBER_STATE_READY;
            if (subscriber->callback.OnServiceConnected != NULL)
                subscriber->callback.OnServiceConnected(subscriber->callback.deviceObject, record->publisher);
           
       
   
    OsalMutexUnlock(&record->obsRecMutex);

其实就是在遍历订阅者链表,回调其callback部分的OnServiceConnected()函数。

订阅者链表里的每个节点是一个HdfServiceSubscriber,示意图如下:

以上这些其实都体现了鸿蒙系统里的一个观念,那就是“设备”其实可以被理解为“服务”。在单机系统里,一个设备的驱动程序可以被理解为一种特殊的库,上层软件通过类似函数调用的方式来调用库,从而操作这个设备。但如果要跨机器地操作设备,那么就不能直接调用函数了。一种较好地方式是将目标设备包装成一个逻辑上的服务,然后供大家使用。所以就必须把“驱动层次”和“服务层次”关联起来,这才有了前文所说的那么多数据机构。现在我们画一张大一点的示意图,把以上概念串一下:

图中画出了体现“设备(驱动)层次”和“服务层次”的两大管理者——DevmgrService和DevSvcManager,可供大家参考。


1.2 发布Local Service部分

看完了发布Public Service的部分,我们接着看HdfDeviceNodePublishService()里发布Local Service的部分。此时调用的是HdfDeviceNodePublishLocalService()。
【drivers/hdf/frameworks/core/host/src/Hdf_device_node.c】

static int HdfDeviceNodePublishLocalService(
    struct HdfDeviceNode *devNode, const struct HdfDeviceInfo *deviceInfo)
    uint32_t matchId;
    . . . . . .
    struct DevHostService *hostService = devNode->hostService;
    . . . . . .
    matchId = HdfMakeHardwareId(deviceInfo->hostId, deviceInfo->deviceId);
    return HdfServiceObserverPublishService(&hostService->observer, deviceInfo->svcName,
        matchId, deviceInfo->policy, (struct HdfObject *)devNode->deviceObject.service);

请注意,虽然也是在调用HdfServiceObserverPublishService(),但传入的第一个参数是&hostService->observer。也就是说,Public Service对应的监听部分,记录在DevSvcManager里,而Local Service对应的监听部分,则记录在其所属的DevHostService里。

现在我们可以画一张发布驱动的调用关系图:



2 挂接设备

我们回过头继续说前文的HdfDeviceLaunchNode()部分。该函数在调用完HdfDeviceNodePublishService()之后,接着就会调用DevmgrServiceClntAttachDevice()。

【drivers/hdf/frameworks/core/host/src/Devmgr_service_clnt.c】

int DevmgrServiceClntAttachDevice(const struct HdfDeviceInfo *deviceInfo, struct IHdfDeviceToken *deviceToken)
    struct IDevmgrService *devMgrSvcIf = NULL;
    struct DevmgrServiceClnt *inst = DevmgrServiceClntGetInstance();
    . . . . . .
    devMgrSvcIf = inst->devMgrSvcIf;
. . . . . .
// 实际调用的是 DevmgrServiceAttachDevice()
    return devMgrSvcIf->AttachDevice(devMgrSvcIf, deviceInfo, deviceToken);

此处调用了DevmgrServiceAttachDevice():
【drivers/hdf/frameworks/core/manager/src/Devmgr_service.c】

static int DevmgrServiceAttachDevice(struct IDevmgrService *inst, 
const struct HdfDeviceInfo *deviceInfo, struct IHdfDeviceToken *token)
    . . . . . .
struct DevHostServiceClnt *hostClnt = DevmgrServiceFindDeviceHost(inst, deviceInfo->hostId);
    . . . . . .
    struct IDevHostService *hostService = hostClnt->hostService;
    . . . . . .
    struct DeviceTokenClnt *tokenClnt = DeviceTokenClntNewInstance(token);
    . . . . . .
    tokenClnt->deviceInfo = deviceInfo;
    HdfSListAdd(&hostClnt->devices, &tokenClnt->node);
    return HDF_SUCCESS;

主要就是向对应的DevHostServiceClnt的devices链表里,添加一个DeviceTokenClnt节点。简单地说就是,一个DevHostServiceClnt和一个DevHostService对应,每当向DevHostService里添加一个HdfDevice节点,相应地就需要在DevHostServiceClnt里添加一个DeviceTokenClnt节点。该节点的tokenIf域记录的IHdfDeviceToken指针,来自于DeviceNodeExt的token域。

说起来,DeviceNodeExt的token其实在DeviceNodeExt构造之时就创建了:
【drivers/hdf/frameworks/core/host/src/Hdf_device_node.c】

void HdfDeviceNodeConstruct(struct HdfDeviceNode *devNode)
    if (devNode != NULL)
        struct IDeviceNode *nodeIf = &devNode->super;
        HdfDeviceObjectConstruct(&devNode->deviceObject);
        devNode->token = HdfDeviceTokenNewInstance();
        nodeIf->LaunchNode = HdfDeviceLaunchNode;
        nodeIf->PublishService = HdfDeviceNodePublishPublicService;
   

其中创建token时,调用的是HdfDeviceTokenNewInstance()。

【drivers/hdf/frameworks/core/host/src/Hdf_device_token.c】

struct IHdfDeviceToken *HdfDeviceTokenNewInstance()
return (struct IHdfDeviceToken *)HdfObjectManagerGetObject(HDF_OBJECT_ID_DEVICE_TOKEN);

【drivers/hdf/frameworks/core/host/src/Hdf_device_token.c】

struct HdfObject *HdfDeviceTokenCreate()
    struct HdfDeviceToken *token = (struct HdfDeviceToken *)OsalMemCalloc(sizeof(struct HdfDeviceToken));
    if (token != NULL)
        HdfDeviceTokenConstruct(token);
   
    return (struct HdfObject *)token;

其中HdfDeviceToken的定义如下:

struct HdfDeviceToken
    struct HdfSListNode node;
    struct IHdfDeviceToken super;
;

咦,怎么又有bug的味道,HdfDeviceToken里应该把super放到第一个吧,否则怎么强制转化成struct HdfObject*呢?好在这个bug的危害不太大,后续版本可以调整一下。

现在我们可以再画一张图看看:



3 小结

经过以上分析,我们头脑中已经可以形成一套比较清楚的HDF逻辑结构了。总之就是将“设备(驱动)层次”和“服务层次”联系起来,该加的observer机制加上。好了,这次就先写到这儿,以后我们再补充其他内容。

(我正在参加 CSDN 的【鸿蒙技术征文】活动,请给我点赞支持。)

 


推荐阅读
  • 利用 Jest 和 Supertest 实现接口测试的全面指南
    本文深入探讨了如何使用 Jest 和 Supertest 进行接口测试,通过实际案例详细解析了测试环境的搭建、测试用例的编写以及异步测试的处理方法。 ... [详细]
  • 本文概述了算法的基础概念,包括时间复杂度的计算规则,以及常见的递归算法的时间复杂度分析。同时,详细介绍了数组和链表的基本特性及其操作的时间复杂度,并提供了几个关于链表操作的具体示例。最后,探讨了栈和队列的概念及其应用,包括如何利用这些数据结构解决实际问题。 ... [详细]
  • 头文件duye_epoll.h************************************************************************** ... [详细]
  • 本文详细介绍了 Java 中 com.amazonaws.auth.SystemPropertiesCredentialsProvider 初始化方法的使用方式,并提供了多个实际的代码示例,帮助开发者更好地理解和应用这一方法。 ... [详细]
  • C++ 中的可调用对象及其应用
    本文介绍了 C++ 中的可调用对象概念,包括函数指针、仿函数、类成员函数指针等,并详细探讨了 `std::function` 和 `std::bind` 在统一处理这些可调用对象时的作用和实现方法。 ... [详细]
  • 本文探讨了如何在无向图中寻找一条从指定起点出发,确保不会连续两次访问同一条边的情况下,获得最大成本路径的方法。 ... [详细]
  • 本文探讨了在Laravel框架下执行涉及多个字段的SUM聚合查询时遇到的问题,特别是当某些关联表中的值为空时,导致最终结果为null的情况。 ... [详细]
  • 在研究Linux内核代码时,经常会遇到与‘队列’相关的术语。本文旨在全面介绍Linux系统中几种常见的队列类型及其应用,帮助读者更好地理解和使用这些机制。 ... [详细]
  • 本文详细介绍了在使用EmguCV进行图像处理时常用的函数及其应用场景,旨在帮助开发者更好地理解和利用这些工具。 ... [详细]
  • 题目来源:http://www.spoj.com/problems/TSUM/。题目描述:给定一个包含N个不同整数的序列,计算所有可能的三个不同索引上的整数之和,并统计每个和出现的不同三元组数量。 ... [详细]
  • Consul 单节点与集群环境构建指南
    本文详细介绍了如何安装和配置 Consul 以支持服务注册与发现、健康检查等功能,包括单节点和集群环境的搭建步骤。 ... [详细]
  • 本文深入探讨了Java中的代理模式,包括静态代理和动态代理的概念、实现及其应用场景。通过具体的代码示例,详细解析了如何利用代理模式增强代码的功能性和灵活性。 ... [详细]
  • 本文通过C++代码示例,详细介绍了如何利用邻接矩阵构建无向图,并实现图的深度优先遍历(DFS)。文章包括了完整的代码实现,以及对关键函数的解释。 ... [详细]
  • 当我在命令行登录MySQL后,无法访问任何依赖数据库连接的PHP应用。此问题每次都会出现。以下是复现问题的具体步骤及解决方案。 ... [详细]
  • 数据结构双链表和双循环链表(C语言代码)
    双链表和双循环链表单链表双循环链表单链表单向链表特点:  1.可以轻松的到达下一个节点,但是回到前一个节点是很难的.  2.只能从头遍历到尾或者从尾遍历到头(一般从头 ... [详细]
author-avatar
被盗不玩了
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有